﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Net;

namespace SystemHelperLibrary.API.CredUI
{
	public class CredUiHelper
	{
		[Flags]
		internal enum CREDUI_FLAGS
		{
			INCORRECT_PASSWORD = 0x1,
			DO_NOT_PERSIST = 0x2,
			REQUEST_ADMINISTRATOR = 0x4,
			EXCLUDE_CERTIFICATES = 0x8,
			REQUIRE_CERTIFICATE = 0x10,
			SHOW_SAVE_CHECK_BOX = 0x40,
			ALWAYS_SHOW_UI = 0x80,
			REQUIRE_SMARTCARD = 0x100,
			PASSWORD_ONLY_OK = 0x200,
			VALIDATE_USERNAME = 0x400,
			COMPLETE_USERNAME = 0x800,
			PERSIST = 0x1000,
			SERVER_CREDENTIAL = 0x4000,
			EXPECT_CONFIRMATION = 0x20000,
			GENERIC_CREDENTIALS = 0x40000,
			USERNAME_TARGET_CREDENTIALS = 0x80000,
			KEEP_USERNAME = 0x100000,
		}

		public enum CredUIReturnCodes
		{
			NO_ERROR = 0,
			ERROR_CANCELLED = 1223,
			ERROR_NO_SUCH_LOGON_SESSION = 1312,
			ERROR_NOT_FOUND = 1168,
			ERROR_INVALID_ACCOUNT_NAME = 1315,
			ERROR_INSUFFICIENT_BUFFER = 122,
			ERROR_INVALID_PARAMETER = 87,
			ERROR_INVALID_FLAGS = 1004,
			ERROR_BAD_ARGUMENTS = 160
		}

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
		private struct CREDUI_INFO
		{
			public int cbSize;
			public IntPtr hwndParent;
			public string pszMessageText;
			public string pszCaptionText;
			public IntPtr hbmBanner;
		}

		[DllImport("ole32.dll")]
		internal static extern void CoTaskMemFree(IntPtr ptr);

		[DllImport("credui.dll", EntryPoint = "CredUIConfirmCredentialsW", CharSet = CharSet.Unicode)]
		private static extern CredUIReturnCodes CredUIConfirmCredentials(string targetName, [MarshalAs(UnmanagedType.Bool)] bool confirm);


		[DllImport("credui.dll", CharSet = CharSet.Auto)]
		private static extern int CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere, int authError, ref uint authPackage,
																	IntPtr InAuthBuffer, uint InAuthBufferSize, out IntPtr refOutAuthBuffer,
																	out uint refOutAuthBufferSize, ref bool fSave, int flags);

		[DllImport("credui.dll", EntryPoint = "CredUIParseUserNameW", CharSet = CharSet.Unicode)]
		private static extern CredUIReturnCodes CredUIParseUserName(string userName, StringBuilder user, int userMaxChars,
																	StringBuilder domain, int domainMaxChars);

		[DllImport("credui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
		private static extern Boolean CredPackAuthenticationBuffer(int dwFlags, string pszUserName, string pszPassword,
																	  IntPtr pPackedCredentials, ref int pcbPackedCredentials);

		[DllImport("credui.dll", CharSet = CharSet.Auto)]
		private static extern bool CredUnPackAuthenticationBuffer(int dwFlags, IntPtr pAuthBuffer, uint cbAuthBuffer, StringBuilder pszUserName,
																	ref int pcchMaxUserName, StringBuilder pszDomainName, ref int pcchMaxDomainame,
																	StringBuilder pszPassword, ref int pcchMaxPassword);


		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
		public static bool ParseUserName(string userName, out string user, out string domain)
		{
			if (string.IsNullOrEmpty(userName))
				throw new ArgumentNullException("userName");

			StringBuilder userBuilder = new StringBuilder();
			StringBuilder domainBuilder = new StringBuilder();

			CredUIReturnCodes returnCode = CredUIParseUserName(userName, userBuilder, int.MaxValue, domainBuilder, int.MaxValue);
			switch (returnCode)
			{
				case CredUIReturnCodes.NO_ERROR: // The username is valid.
					user = userBuilder.ToString();
					domain = domainBuilder.ToString();
					return true;

				case CredUIReturnCodes.ERROR_INVALID_ACCOUNT_NAME: // The username is not valid.
					user = userName;
					domain = null;
					return false;

				case CredUIReturnCodes.ERROR_INVALID_PARAMETER: // ulUserMaxChars or ulDomainMaxChars is zero OR userName, user, or domain is NULL.
					throw new ArgumentNullException("userName");

				default:
					user = null;
					domain = null;
					return false;
			}
		}

		public static bool CredPackAuthenticationBufferApi(string username, string password)
		{
			int inCredSize = 1024;
			IntPtr inCredBuffer = Marshal.AllocCoTaskMem(inCredSize);

			if (CredPackAuthenticationBuffer(0, username, password, inCredBuffer, ref inCredSize))
				return true;

			return false;
		}

		public static void GetCredentialsVistaAndUp(string serverName, out NetworkCredential networkCredential)
		{
			CREDUI_INFO credui = new CREDUI_INFO();
			credui.pszCaptionText = "Please enter the credentails for " + serverName;
			credui.pszMessageText = "DisplayedMessage";
			credui.cbSize = Marshal.SizeOf(credui);
			uint authPackage = 0;
			IntPtr outCredBuffer = new IntPtr();
			uint outCredSize;
			bool save = false;
			int result = CredUIPromptForWindowsCredentials(ref credui, 0, ref authPackage, IntPtr.Zero, 0, out outCredBuffer,
															out outCredSize, ref save, 1 /* Generic */);

			var usernameBuf = new StringBuilder(100);
			var passwordBuf = new StringBuilder(100);
			var domainBuf = new StringBuilder(100);

			int maxUserName = 100;
			int maxDomain = 100;
			int maxPassword = 100;
			if (result == 0)
			{
				if (CredUnPackAuthenticationBuffer(0, outCredBuffer, outCredSize, usernameBuf, ref maxUserName, domainBuf, ref maxDomain,
													passwordBuf, ref maxPassword))
				{
					//clear the memory allocated by CredUIPromptForWindowsCredentials 
					CoTaskMemFree(outCredBuffer);
					networkCredential = new NetworkCredential()
					{
						UserName = usernameBuf.ToString(),
						Password = passwordBuf.ToString(),
						Domain = domainBuf.ToString()
					};
					return;
				}
			}

			networkCredential = null;
		}


	}
}
